package com.blue.core.excel;

import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import javax.servlet.http.HttpServletResponse;

import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.DataFormat;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;

/**
 * Excel导出
 *
 * @author zhengjin
 * @since 1.0 2017年12月26日
 */
public class ExcelExporter
{
	private static Logger logger = LoggerFactory.getLogger(ExcelExporter.class);

	private ExcelExporter()
	{
	}

	/**
	 * 导出Excel
	 *
	 * @param clazz 对象类型
	 * @param os 输出流
	 * @param f 获取对象列表回调
	 */
	public static <T> void export(Class<T> clazz, OutputStream os, Function<Integer, List<T>> f)
	{
		Assert.notNull(clazz, "对象类型不能为空");
		Assert.notNull(os, "输出流不能为空");
		Assert.notNull(f, "对象输出不能为空");

		ExcelConfig config = ExcelParser.getInstance().parse(clazz);

		Workbook wb = new HSSFWorkbook();
		CellStyle style = createStyle(wb);
		Sheet sheet = wb.createSheet();
		setHeaderRow(style, sheet, config);

		int i = 1;
		int p = 0;
		List<T> objectList = null;
		do
		{
			objectList = f.apply(p);
			i = setObjectList(wb, style, sheet, objectList, i, config);
			p++;
		} while (objectList != null && !objectList.isEmpty());

		try (OutputStream oos = os)
		{
			wb.write(oos);
		}
		catch (Exception e)
		{
			logger.error(e.getMessage(), e);
		}
	}

	/**
	 * 导出Excel
	 * @param clazz 对象类型
	 * @param objectList 对象列表
	 * @param os 输出流
	 */
	public static <T> void export(Class<T> clazz, List<T> objectList, OutputStream os)
	{
		Assert.notNull(clazz, "对象类型不能为空");
		Assert.notNull(os, "输出流不能为空");

		ExcelConfig config = ExcelParser.getInstance().parse(clazz);

		Workbook wb = new HSSFWorkbook();
		CellStyle style = createStyle(wb);
		Sheet sheet = wb.createSheet();
		setHeaderRow(style, sheet, config);

		int i = 1;
		i = setObjectList(wb, style, sheet, objectList, i, config);

		try (OutputStream oos = os)
		{
			wb.write(oos);
		}
		catch (Exception e)
		{
			logger.error(e.getMessage(), e);
		}
	}

	/**
	 * 下载Excel
	 *
	 * @param clazz 对象类型
	 * @param objectList 对象列表
	 * @param filename 文件名
	 * @param response HttpServletResponse对象
	 */
	public static <T> void export(Class<T> clazz, List<T> objectList, String filename, HttpServletResponse response)
	{
		Assert.hasText(filename, "文件名不能为空");
		Assert.notNull(response, "HttpServletResponse对象不能为空");

		OutputStream os = null;
		try
		{
			os = response.getOutputStream();
			response.addHeader("Content-Disposition", "attachment; filename=" + new String(filename.getBytes("UTF-8"), "ISO8859-1"));
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		response.setContentType("application/x-xls");
		export(clazz, objectList, os);
	}

	/**
	 * 下载Excel
	 *
	 * @param clazz 对象类型
	 * @param filename 文件名
	 * @param response HttpServletResponse对象
	 * @param f 获取对象列表回调
	 */
	public static <T> void export(Class<T> clazz, String filename, HttpServletResponse response, Function<Integer, List<T>> f)
	{
		Assert.hasText(filename, "文件名不能为空");
		Assert.notNull(response, "HttpServletResponse对象不能为空");

		OutputStream os = null;
		try
		{
			os = response.getOutputStream();
			response.addHeader("Content-Disposition", "attachment; filename=" + new String(filename.getBytes("UTF-8"), "ISO8859-1"));
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
		response.setContentType("application/x-xls");
		export(clazz, os, f);
	}

	private static void setHeaderRow(CellStyle style, Sheet sheet, ExcelConfig config)
	{
		Map<Integer, ExcelColumnConfig> columnMap = config.getColumnMap();
		Row headerRow = sheet.createRow(0);
		for (Map.Entry<Integer, ExcelColumnConfig> entry : columnMap.entrySet())
		{
			headerRow.createCell(entry.getKey()).setCellValue(entry.getValue().getName());
		}
		headerRow.setRowStyle(style);
	}

	private static <T> int setObjectList(Workbook wb, CellStyle style, Sheet sheet, List<T> objectList, int i, ExcelConfig config)
	{
		Map<Integer, ExcelColumnConfig> columnMap = config.getColumnMap();
		if (objectList != null && !objectList.isEmpty())
		{
			for (T object : objectList)
			{
				Row row = sheet.createRow(i++);
				for (Map.Entry<Integer, ExcelColumnConfig> entry : columnMap.entrySet())
				{
					Method getter = entry.getValue().getGetter();
					Object value = null;
					try
					{
						value = getter.invoke(object);
					}
					catch (Exception e)
					{
						e.printStackTrace();
					}
					if (value == null || "".equals(value))
						continue;

					Cell cell = row.createCell(entry.getKey());
					setCellValue(cell, value, wb, entry.getValue());

				}
				row.setRowStyle(style);
			}
		}
		return i;
	}

	private static CellStyle createStyle(Workbook wb)
	{
		CellStyle style = wb.createCellStyle();
		style.setWrapText(true);
		return style;
	}

	private static void setCellValue(Cell cell, Object value, Workbook wb, ExcelColumnConfig columnConfig)
	{
		Class<?> clazz = value.getClass();
		if (value instanceof CharSequence)
		{
			cell.setCellValue(value.toString());
		}
		else if (value instanceof Number)
		{
			cell.setCellValue(((Number)value).doubleValue());
		}
		else if (clazz == boolean.class || clazz == Boolean.class)
		{
			cell.setCellValue((Boolean)value);
		}
		else if (value instanceof Date)
		{
			CellStyle style = wb.createCellStyle();
			DataFormat format = wb.createDataFormat();
			style.setDataFormat(format.getFormat(columnConfig.getFormat()));
			cell.setCellStyle(style);
			cell.setCellValue((Date)value);
		}
		else if (clazz == LocalDate.class)
		{
			LocalDate date = (LocalDate)value;
			Calendar calendar = Calendar.getInstance();
			calendar.set(date.getYear(), date.getMonthValue() - 1, date.getDayOfMonth());

			CellStyle style = wb.createCellStyle();
			DataFormat format = wb.createDataFormat();
			style.setDataFormat(format.getFormat(columnConfig.getFormat()));
			cell.setCellStyle(style);
			cell.setCellValue(calendar);
		}
		else if (clazz == LocalDateTime.class)
		{
			LocalDateTime date = (LocalDateTime)value;
			Calendar calendar = Calendar.getInstance();
			calendar.set(date.getYear(), date.getMonthValue() - 1, date.getDayOfMonth(), date.getHour(), date.getMinute(), date.getSecond());

			CellStyle style = wb.createCellStyle();
			DataFormat format = wb.createDataFormat();
			style.setDataFormat(format.getFormat(columnConfig.getFormat()));
			cell.setCellStyle(style);
			cell.setCellValue(calendar);
		}
	}

}
